﻿namespace LizardChess
open System
open System.Linq

module Posn =
    let create(sqs,pcs,trn,bcas,wcas,mhst,iswhite) =
        {
            Sqs = sqs;
            Pcs = pcs;
            Trn = trn;
            Bcas = bcas;
            Wcas = wcas;
            Mhst = mhst;
            IsWhite = iswhite;
        }
    let st() =
        let arrsqs = Array.create 64 -1
        let arrpcs = Array.zeroCreate 32
        let SetPiecesAtStartingPositions() =
            let CreatePiece(name,col,c,r,ID) =
                let ord = Ref.GetOrdFR (c,r)
                let pc = Piece.create(name,ord,col,-1,0,false,ID)
                arrsqs.[ord] <- (int)ID
                arrpcs.[(int)ID] <- pc
            List.iter CreatePiece [(enmName.King, false, 4,7, enmID.BlackKing);
                (enmName.Queen, false, 3,7, enmID.BlackQueen); 
                (enmName.Rook, false, 0,7, enmID.BlackQueensRook);
                (enmName.Rook, false, 7,7, enmID.BlackKingsRook);  
                (enmName.Bishop, false, 2,7, enmID.BlackQueensBishop);
                (enmName.Bishop, false, 5,7, enmID.BlackKingsBishop); 
                (enmName.Knight, false, 1,7, enmID.BlackQueensKnight);
                (enmName.Knight, false, 6,7, enmID.BlackKingsKnight);
                (enmName.Pawn, false, 0,6, enmID.BlackPawn1);
                (enmName.Pawn, false, 1,6, enmID.BlackPawn2);
                (enmName.Pawn, false, 2,6, enmID.BlackPawn3);
                (enmName.Pawn, false, 3,6, enmID.BlackPawn4);
                (enmName.Pawn, false, 4,6, enmID.BlackPawn5);
                (enmName.Pawn, false, 5,6, enmID.BlackPawn6);
                (enmName.Pawn, false, 6,6, enmID.BlackPawn7);
                (enmName.Pawn, false, 7,6, enmID.BlackPawn8);
                (enmName.King, true, 4,0, enmID.WhiteKing);
                (enmName.Queen, true, 3,0, enmID.WhiteQueen);
                (enmName.Rook, true, 0,0, enmID.WhiteQueensRook);
                (enmName.Rook, true, 7,0, enmID.WhiteKingsRook); 
                (enmName.Bishop, true, 2,0, enmID.WhiteQueensBishop);
                (enmName.Bishop, true, 5,0, enmID.WhiteKingsBishop);
                (enmName.Knight, true, 1,0, enmID.WhiteQueensKnight);
                (enmName.Knight, true, 6,0, enmID.WhiteKingsKnight);
                (enmName.Pawn, true, 0,1, enmID.WhitePawn1);
                (enmName.Pawn, true, 1,1, enmID.WhitePawn2);
                (enmName.Pawn, true, 2,1, enmID.WhitePawn3);
                (enmName.Pawn, true, 3,1, enmID.WhitePawn4);
                (enmName.Pawn, true, 4,1, enmID.WhitePawn5); 
                (enmName.Pawn, true, 5,1, enmID.WhitePawn6);
                (enmName.Pawn, true, 6,1, enmID.WhitePawn7);
                (enmName.Pawn, true, 7,1, enmID.WhitePawn8);]
            arrsqs|>List.ofArray,arrpcs|>List.ofArray
        let sqs,pcs = SetPiecesAtStartingPositions()           
        create(sqs,pcs,0,false,false,[],true)
   
    let ToFen(p) =
        // Field 1: Piece placement data
        let fen1 =
            let mutable ans = ""
            let mutable iNbrEmptySquare = 0
            for indRank = 7 downto 0 do
                if indRank <> 7 then
                    ans <- ans+"/"
                for indFile = 0 to 7 do
                    let ordThis = Ref.GetOrdFR(indFile, indRank)
                    let pieceThis = p.Sqs.[ordThis]
                    if (pieceThis = -1) then
                        iNbrEmptySquare<-iNbrEmptySquare+1
                    else
                        if (iNbrEmptySquare > 0) then
                            ans<-ans+iNbrEmptySquare.ToString()
                            iNbrEmptySquare <- 0
                        if (pieceThis>15) then
                            ans<-ans+Piece.abbrev(p.Pcs.[pieceThis].Name).ToLower()
                        else
                            ans<-ans+Piece.abbrev(p.Pcs.[pieceThis].Name)
                if (iNbrEmptySquare > 0) then
                    ans<-ans+iNbrEmptySquare.ToString()
                    iNbrEmptySquare <- 0
            ans
        // Field 2: Active colour
        let fen2 = if (p.Trn%2=0) then " w " else " b "
        // Field 3: Castling availability
        let fen3 =
            let mutable ans = ""
            if p.canK then ans <- "K"
            if p.canQ then ans <- ans+"Q"
            if p.cank then ans <- ans+"k"
            if p.canq then ans <- ans+"q"
            if ans="" then ans <- "-"
            ans    
        // Field 4: En passant target square coordonates
        let fen4 =
            if p.Mhst.Length>0 then
                let lastMove = p.Mhst.[0]
                let lastMovepcid = lastMove.mvPiece
                let lastMovepc = p.Pcs.[lastMovepcid]
                if lastMovepc.Name = enmName.Pawn && Ref.fi.[lastMove.From] = Ref.fi.[lastMove.To] && (((Ref.ri.[lastMove.From] = Ref.ri.[lastMove.To] + 2) && (not lastMovepc.pcIsWhite)) || ((Ref.ri.[lastMove.From] = Ref.ri.[lastMove.To] - 2) && (lastMovepc.pcIsWhite))) then
                    " " + Ref.f.[lastMove.From] + (if (lastMovepc.pcIsWhite) then "3 " else "6 ") // The case between From and To
                else
                    " - "
            else
                " - "// There is not en passant target square
        // Field 5: number of Halfmove clock or ply since the last pawn advance or capturing move.
        let fen5 = String.Format("{0} ", ( if p.Mhst.Length > 0 then p.Mhst.[0].FiftyMoveDrawCounter else 0))
        // Field 6: Full move number
        let fen6 = ((p.Trn >>> 1) + 1).ToString() 
        fen1+fen2+fen3+fen4+fen5+fen6
    let Mpgn p = p.Mhst |> List.map (fun m -> m.PGN)

    let CreateMove(p,mvname,mvpiece,from,mto,piececaptured,score) =
        let mFiftyMoveDrawCounter = if (mvname <> NullMove && piececaptured = -1 && p.Pcs.[mvpiece].Name <> enmName.Pawn) then (if p.Mhst.Length > 0 then p.Mhst.[p.Mhst.Length - 1].FiftyMoveDrawCounter + 1 else 1) else 0
        let mvpc = p.Pcs.[mvpiece]
        Mov.create(mvpiece,mvpc.Name,from,mto,piececaptured,mvname,mFiftyMoveDrawCounter,"","")
    let CanBeMovedToBy(ord,dif,psn,testK) =
        let psn = {psn with IsWhite=if dif then not psn.IsWhite else psn.IsWhite}
        //move based results
        let rec CanBmovs nm ml =
            match ml with
                |[] -> false
                | ml ->  
                    let cord = ml.Head 
                    if psn.Sqs.[cord] = -1 then
                        CanBmovs nm ml.Tail 
                    elif psn.Pcs.[psn.Sqs.[cord]].pcIsWhite = psn.IsWhite && psn.Pcs.[psn.Sqs.[cord]].Name=nm then
                        true
                    else
                        CanBmovs nm ml.Tail
        // Pawn
        let canbMv2P() = CanBmovs enmName.Pawn (if psn.IsWhite then Ref.attsPWto.[ord] else Ref.attsPBto.[ord])
        // Knight
        let canbMv2N() = CanBmovs enmName.Knight Ref.movsN.[ord]
        //get rays based results
        let rec CanBrays nm r rl =
            match r with
                |[] -> if rl=[] then false else CanBrays nm rl.Head rl.Tail
                | r ->  
                    let cord = r.Head 
                    if psn.Sqs.[cord] = -1 then
                        CanBrays nm r.Tail rl 
                    elif psn.Pcs.[psn.Sqs.[cord]].pcIsWhite = psn.IsWhite && (psn.Pcs.[psn.Sqs.[cord]].Name=nm || psn.Pcs.[psn.Sqs.[cord]].Name=enmName.Queen ) then
                        true
                    else
                        if rl=[] then false else CanBrays nm rl.Head rl.Tail
        // Bishop & Queen
        let canbMv2B() = CanBrays enmName.Bishop Ref.raysB.[ord].Head Ref.raysB.[ord].Tail
        // Rook & Queen
        let canbMv2R() = CanBrays enmName.Rook Ref.raysR.[ord].Head Ref.raysR.[ord].Tail
        // King!
        let canbMv2K() = CanBmovs enmName.King Ref.movsK.[ord]
        canbMv2P()||canbMv2N()||canbMv2B()||canbMv2R()||(testK && canbMv2K())
    let is_in_check(psn,testK) =
        let kingsq = if psn.IsWhite then 4 else 20
        CanBeMovedToBy(psn.Pcs.[kingsq].SquareOrd,true,psn,testK)
    let Capture (pcid,psn) =
        let pc = psn.Pcs.[pcid]
        {psn with Sqs=psn.Sqs|>List.mapi (fun i sq -> if i=pc.SquareOrd then -1 else sq);Pcs=psn.Pcs|>List.mapi (fun i pc -> if i=pcid then {pc with SquareOrd= -1} else pc)} 
    let Uncapture(pcid,sqid,psn) =
        let pc = psn.Pcs.[pcid]
        {psn with Sqs=psn.Sqs|>List.mapi (fun i sq -> if i=sqid then pcid else sq);Pcs=psn.Pcs|>List.mapi (fun i pc -> if i=pcid then {pc with SquareOrd=sqid} else pc)} 
    let DoMoveOK(mv,psn) =
        //do moves first
        let pc = psn.Pcs.[mv.mvPiece]
        let pccap = if mv.pieceCaptured <> -1 then psn.Pcs.[mv.pieceCaptured] else Piece.blank
        //do castling
        let pieceRookidOrd = 
            if mv.mvName = CastleKingSide && pc.pcIsWhite then
                Ref.GetOrdFR (7,0)
            elif mv.mvName = CastleKingSide then
                Ref.GetOrdFR (7,7)
            elif mv.mvName = CastleQueenSide && pc.pcIsWhite then
                Ref.GetOrdFR (0,0)
            elif mv.mvName = CastleQueenSide then
                Ref.GetOrdFR (0,7)
            else
                -1
        let pieceRookid = if pieceRookidOrd <> -1 then psn.Sqs.[pieceRookidOrd] else -1
        let newpieceRookidOrd =
            if mv.mvName = CastleKingSide then
                Ref.GetOrdFR(5, Ref.ri.[mv.To])
            elif mv.mvName = CastleQueenSide then
                Ref.GetOrdFR(3, Ref.ri.[mv.To])
            else
                -1
        let pieceRook = if pieceRookid <> -1 then psn.Pcs.[pieceRookid] else Piece.blank
        let newpieceRook = if pieceRookid <> -1 then {pieceRook with SquareOrd=newpieceRookidOrd} else Piece.blank
        //do promotion
        let newpc =
            match mv.mvName with
                |PawnPromotionBishop -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Bishop;HasBeenPromoted=true}
                |PawnPromotionKnight -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Knight;HasBeenPromoted=true}
                |PawnPromotionRook -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Rook;HasBeenPromoted=true}
                |PawnPromotionQueen -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1;Name=enmName.Queen;HasBeenPromoted=true}
                |_ -> {pc with SquareOrd=mv.To;LastMoveTurnNo=psn.Trn;NoOfMoves=pc.NoOfMoves+1}
        //do psn changes
        let npsn = if mv.pieceCaptured <> -1 then Capture(mv.pieceCaptured,psn) else psn
        let npsn = {npsn with Sqs=npsn.Sqs|>List.mapi(fun i sq -> if i=mv.From then -1 else sq)}
        let npsn = {npsn with Sqs=npsn.Sqs|>List.mapi(fun i sq -> if i=mv.To then mv.mvPiece else sq)}
        let npsn = {npsn with Pcs=npsn.Pcs|>List.mapi(fun i pc -> if i=mv.mvPiece then newpc else pc)}
        let npsn = {npsn with Sqs=npsn.Sqs|>List.mapi(fun i sq -> if pieceRookidOrd <> -1 && i=pieceRookidOrd then -1 else sq)}
        let npsn = {npsn with Sqs=npsn.Sqs|>List.mapi(fun i sq -> if pieceRookidOrd <> -1 && i=newpieceRookidOrd then pieceRookid else sq)}
        let npsn = {npsn with Pcs=npsn.Pcs|>List.mapi(fun i pc -> if pieceRookidOrd <> -1 && i=pieceRookid then newpieceRook else pc)}
        //check for invalidmove
        let IsInCheck = is_in_check(npsn,true)
        //Undo invalid moves
        if IsInCheck then
            false,psn
        else
            //do castling
            let nWcas = if (mv.mvName = CastleKingSide||mv.mvName = CastleQueenSide) && pc.pcIsWhite then true else npsn.Wcas
            let nBcas = if (mv.mvName = CastleKingSide||mv.mvName = CastleQueenSide) && not pc.pcIsWhite then true else npsn.Bcas
            true,{npsn with Trn=npsn.Trn+1;Bcas=nBcas;Wcas=nWcas;Mhst=mv::npsn.Mhst;IsWhite=not npsn.IsWhite}

    let CanCastle(kingsq,ks,psn) =
        let pc = psn.Pcs.[kingsq]
        let pccol = pc.pcIsWhite
        let pcsqord = pc.SquareOrd
        // King hasnt moved
        let testkm() = 
            if pccol then
                if ks then not psn.canK else not psn.canQ
            else 
                if ks then not psn.cank else not psn.canq
        // All squares between King and Rook are unoccupied
        let testemp() = 
            if ks then
                (psn.Sqs.[pcsqord + 1] <> -1 || psn.Sqs.[pcsqord + 2] <> -1) 
            else
                (psn.Sqs.[pcsqord - 1] <> -1 || psn.Sqs.[pcsqord - 2] <> -1 || psn.Sqs.[pcsqord - 3] <> -1)  
        // The king does not move over a square that is attacked by an enemy piece during the castling move
        let testattck() = 
            //need to send other as true indicating testing wrong player to move to try
            let oth = true
            if ks then
                CanBeMovedToBy(pcsqord+1,oth,psn,true) || CanBeMovedToBy(pcsqord+2,oth,psn,true) 
            else
                CanBeMovedToBy(pcsqord-1,oth,psn,true) || CanBeMovedToBy(pcsqord-2,oth,psn,true)
        // King is not in check or above
        if testkm() || testemp() || testattck() || is_in_check(psn,false) then false else true
    let CanCastleK(psn) = CanCastle(4,true,psn)
    let CanCastleQ(psn) = CanCastle(4,false,psn)
    let CanCastlek(psn) = CanCastle(20,true,psn)
    let CanCastleq(psn) = CanCastle(20,false,psn)
    let gen_lazy_moves pcid psn =
        let pc = psn.Pcs.[pcid]
        let pccol = pc.pcIsWhite
        let pcsqord = pc.SquareOrd
        //ray based moves
        let rec findMovesRays r rl ml =
            match r with
                |[] -> if rl<>[] then findMovesRays rl.Head rl.Tail ml else ml
                | r ->  
                    let cord = r.Head 
                    if psn.Sqs.[cord] = -1 then
                        let oml = (CreateMove(psn,Standard, pcid, pc.SquareOrd, cord, -1, 0))::ml
                        findMovesRays r.Tail rl oml
                    elif (psn.Pcs.[psn.Sqs.[cord]].pcIsWhite <> pccol && psn.Pcs.[psn.Sqs.[cord]].Name<>enmName.King) then
                        let oml = (CreateMove(psn,Standard, pcid, pc.SquareOrd, cord, psn.Sqs.[cord], 0))::ml
                        if rl<>[] then findMovesRays rl.Head rl.Tail oml else oml
                    else
                        if rl<>[] then findMovesRays rl.Head rl.Tail ml else ml
        //do functions for OK moves, captures, non-captures
        let moveOK ord = 
            let pccapid = psn.Sqs.[ord]
            if pccapid = -1 || (psn.Pcs.[pccapid].pcIsWhite <> pccol && psn.Pcs.[pccapid].Name<>enmName.King) then 
                [(CreateMove(psn,Standard, pcid, pcsqord, ord, pccapid, 0))]
            else []
        let capOKt ord typ = 
            let pccapid = psn.Sqs.[ord]
            if pccapid <> -1 && psn.Pcs.[pccapid].pcIsWhite <> pccol && psn.Pcs.[pccapid].Name<>enmName.King then 
                [(CreateMove(psn,typ, pcid, pcsqord, ord, pccapid, 0))]
            else []
        let capOK ord = capOKt ord Standard 
        let noncapOKt ord typ = 
            let pccapid = psn.Sqs.[ord]
            if pccapid = -1 then 
                [(CreateMove(psn,typ, pcid, pcsqord, ord, pccapid, 0))]
            else []
        let noncapOK ord = noncapOKt ord Standard 
        match pc.Name with
            |enmName.Pawn -> 
                let blnIsPromotion = if pccol then Ref.promPW.[pcsqord] else Ref.promPB.[pcsqord]
                if blnIsPromotion then
                    let PromotionTypes = [PawnPromotionQueen;PawnPromotionRook;PawnPromotionKnight;PawnPromotionBishop]
                    // Captures
                    let capords = if pccol then Ref.attsPW.[pcsqord] else Ref.attsPB.[pcsqord]
                    let caps = capords |> List.map (fun ord -> List.map (capOKt ord) PromotionTypes|>List.concat)|>List.concat
                    // Forward one
                    let ord = if pccol then Ref.movPW.[pcsqord] else Ref.movPB.[pcsqord]
                    let mvs = List.map (noncapOKt ord) PromotionTypes|>List.concat
                    caps@mvs
                else
                    // Captures
                    let caps = List.map capOK (if pccol then Ref.attsPW.[pcsqord] else Ref.attsPB.[pcsqord])|>List.concat
                    // Forward one
                    let ord = if pccol then Ref.movPW.[pcsqord] else Ref.movPB.[pcsqord]
                    let f1s = noncapOK ord
                    // Forward two
                    let ord2 = if pccol then Ref.movPW2.[pcsqord] else Ref.movPB2.[pcsqord]
                    let f2s =
                        if ord2 <> -1 then
                            // Check one square ahead is not occupied
                            let pccapid = psn.Sqs.[ord]
                            if (pccapid = -1) then noncapOK ord2 else []
                        else []
                    // En Passent Left
                    let epl =
                        if (Ref.epPWl.[pcsqord] && pccol || Ref.epPBl.[pcsqord] && not pccol) then
                            let piecePassedid = psn.Sqs.[Ref.epPlpc.[pcsqord]]
                            if (piecePassedid <> -1 && psn.Pcs.[piecePassedid].NoOfMoves = 1 && psn.Pcs.[piecePassedid].LastMoveTurnNo = psn.Trn-1 && psn.Pcs.[piecePassedid].Name = enmName.Pawn && psn.Pcs.[piecePassedid].pcIsWhite <> pc.pcIsWhite) then
                                let ord = if pccol then Ref.epPWlto.[pcsqord] else Ref.epPBlto.[pcsqord]
                                [(CreateMove(psn,EnPassent, pcid, pcsqord, ord, piecePassedid, 0))]
                            else []
                        else []
                    // En Passent Right
                    let epr =
                        if (Ref.epPWr.[pcsqord] && pccol || Ref.epPBr.[pcsqord] && not pccol) then
                            let piecePassedid = psn.Sqs.[Ref.epPrpc.[pcsqord]]
                            if (piecePassedid <> -1 && psn.Pcs.[piecePassedid].NoOfMoves = 1 && psn.Pcs.[piecePassedid].LastMoveTurnNo = psn.Trn-1 && psn.Pcs.[piecePassedid].Name = enmName.Pawn && psn.Pcs.[piecePassedid].pcIsWhite <> pc.pcIsWhite) then
                                let ord = if pccol then Ref.epPWrto.[pcsqord] else Ref.epPBrto.[pcsqord]
                                [(CreateMove(psn,EnPassent, pcid, pc.SquareOrd, ord, piecePassedid, 0))]
                            else []
                        else []
                    caps@f1s@f2s@epl@epr
            |enmName.Bishop -> 
                findMovesRays Ref.raysB.[pcsqord].Head Ref.raysB.[pcsqord].Tail []
            |enmName.Knight ->
                let ords = Ref.movsN.[pcsqord]
                List.map moveOK ords|>List.concat
            |enmName.Rook -> 
                findMovesRays Ref.raysR.[pcsqord].Head Ref.raysR.[pcsqord].Tail []
            |enmName.Queen -> 
                findMovesRays Ref.raysQ.[pcsqord].Head Ref.raysQ.[pcsqord].Tail []
            |enmName.King ->
                let ords = Ref.movsK.[pcsqord]
                let mvs = List.map moveOK ords|>List.concat
                let cK = if (pccol && CanCastleK(psn)) then [(CreateMove(psn,CastleKingSide, pcid, pcsqord, pcsqord + 2, -1, 0))] else []
                let ck = if (not pccol && CanCastlek(psn)) then [(CreateMove(psn,CastleKingSide, pcid, pcsqord, pcsqord + 2, -1, 0))] else []
                let cQ = if (pccol && CanCastleQ(psn)) then [(CreateMove(psn,CastleQueenSide, pcid, pcsqord, pcsqord - 2, -1, 0))] else []
                let cq = if (not pccol && CanCastleq(psn)) then [(CreateMove(psn,CastleQueenSide, pcid, pcsqord, pcsqord - 2, -1, 0))] else []
                mvs@cK@ck@cQ@cq
            |_ -> raise InvalidName
    let GenerateLegalMoves(pc,psn) = (gen_lazy_moves pc psn)|>List.filter (fun m -> fst(DoMoveOK(m,psn)))
    /// generates all legal moves from a position
    let GenerateLegalMovesPr psn =
        if psn.IsWhite then
            [0..15]|>List.map(fun i -> if psn.Pcs.[i].SquareOrd <> -1 then GenerateLegalMoves(i,psn) else [])|>List.concat
        else
            [16..31]|>List.map(fun i -> if psn.Pcs.[i].SquareOrd <> -1 then GenerateLegalMoves(i,psn) else [])|>List.concat
    let IsInCheckMate psn =
        //this is only called if in check is known
        //check for any pc
        let checkpc pc =
            let mvl = gen_lazy_moves pc psn
            let rec validmv ml =
                if ml=[] then
                    false
                else
                    let move = ml.Head
                    if fst(DoMoveOK(move,psn)) then
                        true                        
                    else
                        validmv ml.Tail
            validmv mvl
        //function that returns if a legal move is found
        let bot,top = if psn.IsWhite then 0,15 else 16,31
        let rec nolegalexists i =
            if i>top then 
                true
            elif psn.Pcs.[i].SquareOrd <> -1 then 
                if checkpc i then
                    false 
                else 
                    nolegalexists (i+1)
            else
                nolegalexists (i+1)
        nolegalexists bot
    let DoMove (mvi,ipsn) =
        let strfrom = Ref.sq.[mvi.From]
        let strto = Ref.sq.[mvi.To]
        let moves = GenerateLegalMovesPr ipsn
        let dupfrom = ref ""
        let rec testdup mvl =
            match mvl with
                |[] ->  ()
                |_  ->  let move = mvl.Head
                        if Ref.sq.[move.To]=strto && Piece.abbrev(mvi.mvPcName)=Piece.abbrev(move.mvPcName) && Ref.sq.[move.From]<>strfrom  then
                            dupfrom:=Ref.sq.[move.From]
                        else testdup mvl.Tail
        testdup moves
        let _,psn = DoMoveOK(mvi,ipsn)
        let incheck = is_in_check(psn,false)
        let incheckmate = if incheck then IsInCheckMate(psn) else false
        //update mv in history
        let mv = psn.Mhst.Head
        let pgn = Mov.PGN(mv,!dupfrom,incheckmate,incheck)
        let uci = Mov.uci(mv)
        {psn with Mhst={mv with PGN=pgn;UCI=uci}::psn.Mhst.Tail}
    let rec DoMoves (mvl,ipsn) =
        if mvl = [] then ipsn
        else
            let opsn = DoMove (mvl.Head,ipsn)
            DoMoves (mvl.Tail,opsn)
    //used by UI to get index of image
    let imageIndex (i,psn) = 
        let c = psn.Pcs.[i].pcIsWhite
        let o = psn.Pcs.[i].Name
        match (c,o) with
            |(false,enmName.Bishop) -> 0
            |(true,enmName.Bishop) -> 1
            |(false,enmName.King) -> 2
            |(true,enmName.King) -> 3
            |(false,enmName.Knight) -> 4
            |(true,enmName.Knight) -> 5
            |(false,enmName.Pawn) -> 6
            |(true,enmName.Pawn) -> 7
            |(false,enmName.Queen) -> 8
            |(true,enmName.Queen) -> 9
            |(false,enmName.Rook) -> 10
            |(true,enmName.Rook) -> 11
            |_ -> raise InvalidName
    /// Do a move on a position given a from and a to
    let DoMoveft ((mfrom, mto, mn) ,psn) =
        let moves = GenerateLegalMovesPr psn
        let rec fndmv (mvl:move list) =
            if mvl =[] then Mov.blank
            else
                let mv = mvl.Head
                if (mn=oMoveName.NullMove && mv.From = mfrom && mv.To=mto) ||
                    (mv.mvName=mn && mv.From = mfrom && mv.To=mto) then 
                     mv else fndmv mvl.Tail
        let mv = fndmv moves
        DoMove (mv,psn)
    /// Do a list of moves on a position given a from and a to
    let rec DoMovesft ((mvlft:(int*int*oMoveName) list),ipsn) =
        if mvlft = [] then ipsn
        else
            let opsn = DoMoveft (mvlft.Head,ipsn)
            DoMovesft (mvlft.Tail,opsn)
    /// Find a move given UCI move and a position
    let FndMv ((ucimv:string),psn) =
        let moves = GenerateLegalMovesPr psn
        let f,t,mn = Mov.fromuci ucimv 
        let rec fndmv (mvl:move list) =
            if mvl =[] then Mov.blank
            else
                let mv = mvl.Head
                if (mn=oMoveName.NullMove && mv.From=f && mv.To=t) ||
                    (mv.mvName=mn && mv.From=f && mv.To=t) then 
                     mv else fndmv mvl.Tail
        fndmv moves
    /// mvl2str - generates array of string of moves from movlist
    let rec mvl2str (mvl:move list) str =
        if mvl=[] then str
        else mvl2str mvl.Tail (str + " " + (mvl.Head.UCI))
    /// psn2str - generates array of string of moves from position
    let rec psn2str (psn:posn) = (mvl2str (List.rev psn.Mhst) "").Trim()
